package com.bfb;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.json.JSONArray;
import org.json.JSONObject;

import android.app.Activity;
import android.app.ProgressDialog;
import android.database.DataSetObserver;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Base64;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.CheckBox;
import android.widget.Gallery;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import android.widget.Toast;

import com.bfb.adapters.DishAdapter;
import com.bfb.adapters.IngredientListAdapter;
import com.bfb.helpers.Ingredients;
import com.bfb.helpers.ServerUtilities;
import com.bfb.helpers.User;
import com.facebook.android.DialogError;
import com.facebook.android.Facebook;
import com.facebook.android.Facebook.DialogListener;
import com.facebook.android.FacebookError;

public class UserProfileActivity extends Activity {

	private static final Facebook facebook = Main.facebook;
	private User user = Main.user;
	ProgressDialog pdia;
	AutoCompleteTextView ingredientNameField;
	ArrayAdapter<String> ingredientNameAutoCompleteAdapter;
	ListView ingredientList;
	IngredientListAdapter ingredientListAdapter;
	private DishAdapter dishAdapter;
	private ArrayList<String> ingredients = new ArrayList<String>();

	/**
	 * This sole prupose of this method is to able to access the user variable
	 * from the unit tests
	 * 
	 * @return User
	 */
	public User getUser() {
		return user;
	}

	/**
	 * This sole prupose of this method is to able to access the facebook
	 * variable from the unit tests
	 * 
	 * @return Facebook
	 */
	public Facebook getFacebook() {
		return facebook;
	}

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.edit_profile);
		initIngredientList();
		// initIngredientsField();
		new IngredientsAsyncTask().execute();
		initCheckBoxes();

		if (user.getfirstTimeToEdit()) {// Check if this is the first time
										// during
										// this run of the app for the user to
										// open edit profile
										// We do this check to avoid making a
										// server call every time the user opens
										// edit profile
			user.setFirstTimeToEdit(false);
			new getFavDishes().execute(); // Get the users favorite dishes from
											// the server
		} else {
			setUserImage(user.getProfilePicture());
			setUserName(user.getUserName());
			initGallery();
		}
		// In case the user has FB access enabled and his username is not set a
		// FB call is made to set his user name and profile picture
		if (user.getAllowFB()
				&& (user.getUserName() == null
						|| user.getUserName().equals("N\\A") || user
						.getUserName().length() == 0))
			fbAuthorize();

	}

	/**
	 * This method initializes the list of ingredients the user hates. The list
	 * populated with ingredients.
	 */
	private void initIngredientList() {
		ingredientList = (ListView) findViewById(R.id.hateIngList);
		ingredientListAdapter = new IngredientListAdapter(this,
				user.getHateIngs());
		ingredientListAdapter.registerDataSetObserver(new DataSetObserver() {
			@Override
			public void onChanged() {
				super.onChanged();
				if (ingredientListAdapter.add) {
					ingredientNameAutoCompleteAdapter
							.remove(ingredientListAdapter.addedIng);
				} else {
					ingredientNameAutoCompleteAdapter
							.add(ingredientListAdapter.removedIng);
				}
			}
		});
		ingredientList.setAdapter(ingredientListAdapter);
	}

	/**
	 * This method is to initialize the autocomplete field for the ingredients
	 */
	private void initIngredientsField() {
		ingredientNameField = (AutoCompleteTextView) findViewById(R.id.hateIngsAutocomplete);
		ingredientNameAutoCompleteAdapter = new ArrayAdapter<String>(this,
				R.layout.list_item, ingredients);
		ingredientNameField.setAdapter(ingredientNameAutoCompleteAdapter);
		ingredientNameField.setOnItemClickListener(new OnItemClickListener() {
			@Override
			public void onItemClick(AdapterView<?> parent, View view,
					int position, long id) {
				String selected = ingredientNameAutoCompleteAdapter
						.getItem(position);
				ingredientListAdapter.add(selected);
				ingredientNameField.setText("");
			}
		});
		ingredientNameField
				.setOnEditorActionListener(new OnEditorActionListener() {
					@Override
					public boolean onEditorAction(TextView v, int actionId,
							KeyEvent event) {
						String text = v.getText().toString();
						if (!text.equals("")) {
							String firstChar = text.charAt(0) + "";
							text = text.replaceFirst(firstChar,
									firstChar.toUpperCase());
							if (ingredientNameAutoCompleteAdapter
									.getPosition(text) == -1) {
								Toast.makeText(getBaseContext(),
										"Ingredient not found!",
										Toast.LENGTH_LONG).show();
							}
						}
						return false;
					}
				});
	}

	/**
	 * This method initializes the gallery responsible for displaying the users
	 * favorite dishes. In case the user does not have any favorite dishes a
	 * TextView is made visible instead stating that the user has no favorite
	 * dishes
	 */
	private void initGallery() {
		if (user.getFavoriteDishes().size() == 0) {
			findViewById(R.id.noFavDishesLabel).setVisibility(View.VISIBLE);
			findViewById(R.id.favDishesGallery).setVisibility(View.INVISIBLE);
		} else {
			findViewById(R.id.noFavDishesLabel).setVisibility(View.INVISIBLE);
			findViewById(R.id.favDishesGallery).setVisibility(View.VISIBLE);
			Gallery gallery = (Gallery) findViewById(R.id.favDishesGallery);
			dishAdapter = new DishAdapter(getBaseContext(), gallery,
					user.getFavoriteDishes());
			gallery.setAdapter(dishAdapter);
			gallery.setOnItemClickListener(((DishAdapter) gallery.getAdapter()).listener);
		}

	}

	/**
	 * Sets a text view with the user's userName
	 * 
	 * @param name
	 */
	public void setUserName(String name) {
		((TextView) findViewById(R.id.userName)).setText(name);
	}

	/**
	 * Sets the check boxes in the edit profile page with the preferences set by
	 * the user
	 */
	private void initCheckBoxes() {
		if (facebook.isSessionValid() || user.getAllowFB())
			((CheckBox) findViewById(R.id.fbAccessCheckBox)).setChecked(true);
		else
			((CheckBox) findViewById(R.id.fbAccessCheckBox)).setChecked(false);
		((CheckBox) findViewById(R.id.neverShowCheckBox)).setChecked(user
				.getNeverShow());
	}

	/**
	 * Retrieves the user's profile picture from facebook. In case an exception
	 * occurs the user's profile picture is set to null
	 * 
	 * @param userID
	 * @return {@link Bitmap}
	 */
	public Bitmap getUserPic(String userID) {
		String imageURL;
		Log.d("", "Loading Picture");
		imageURL = "http://graph.facebook.com/" + userID
				+ "/picture?type=small";
		Bitmap userImage = null;
		try {
			userImage = BitmapFactory.decodeStream((InputStream) new URL(
					imageURL).getContent());
			user.setProfilePicture(userImage);
		} catch (Exception e) {
			Log.d("TAG", "Loading Picture FAILED");
			e.printStackTrace();
			user.setProfilePicture(null);
		}
		return userImage;
	}

	/**
	 * Sets an ImageView with the user's profile picture. In case the user does
	 * not have a profile picture a default is used.
	 * 
	 * @param bitmap
	 */
	public void setUserImage(Bitmap bitmap) {

		TextView userNameTextView = (TextView) findViewById(R.id.userName);
		Drawable d;
		if (bitmap == null) {
			d = getResources().getDrawable(R.drawable.default_user);
			d.setBounds(0, 0, d.getMinimumWidth(), d.getMinimumHeight());

			userNameTextView.setCompoundDrawables(d, null, null, null);

		} else {
			d = new BitmapDrawable(bitmap);
			d.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight());

			userNameTextView.setCompoundDrawables(d, null, null, null);
		}
		// ImageView imageView = (ImageView) findViewById(R.id.userImg);
		// if (bitmap == null)
		// imageView.setImageResource(R.drawable.default_user);
		// else
		// imageView.setImageBitmap(bitmap);
	}

	/**
	 * This method is the begining of a series of facebook calls. In case the
	 * facebook session is invalid an authorization is made. Then oncomplete an
	 * attempt is made to retrieve the user's basic info; username and id. After
	 * that an attempt is made to retrieve the user's profile picture
	 */
	public void fbAuthorize() {
		if (!facebook.isSessionValid())
			facebook.authorize(this, new DialogListener() {
				@Override
				public void onComplete(Bundle values) {
					user.setAllowFB(true);
					((CheckBox) findViewById(R.id.fbAccessCheckBox))
							.setChecked(user.getAllowFB());
					new FBUserInfo().execute();

				}

				@Override
				public void onFacebookError(FacebookError error) {
					user.setAllowFB(false);
					((CheckBox) findViewById(R.id.fbAccessCheckBox))
							.setChecked(user.getAllowFB());
				}

				@Override
				public void onError(DialogError e) {
					user.setAllowFB(false);
					((CheckBox) findViewById(R.id.fbAccessCheckBox))
							.setChecked(user.getAllowFB());
				}

				@Override
				public void onCancel() {
					user.setAllowFB(false);
					((CheckBox) findViewById(R.id.fbAccessCheckBox))
							.setChecked(user.getAllowFB());
				}
			});
		else
			new FBUserInfo().execute();
	}

	/**
	 * Listens to events of clicking on the checkbox responsible for Facebook
	 * access. If the current value is true, an attempt is made to log into
	 * facebook, in case the user name was not set an attempt is made to retrive
	 * the data from facebook. If for any reason an error occurs then facebook
	 * access is set to false. If the current value is false the user is logged
	 * out from facebook and allow permissions are removed.
	 * 
	 * @param view
	 */
	public void handleFBCheckBox(View view) {
		boolean newvalue = ((CheckBox) findViewById(R.id.fbAccessCheckBox))
				.isChecked();

		if (newvalue) {
			facebook.authorize(this, new String[] { "publish_stream" },
					new DialogListener() {
						@Override
						public void onComplete(Bundle values) {
							user.setAllowFB(true);
							((CheckBox) findViewById(R.id.fbAccessCheckBox))
									.setChecked(true);
							if (user.getUserName() == null
									|| user.getUserName().equals("N\\A")
									|| user.getUserName().length() == 0)
								new FBUserInfo().execute();
						}

						@Override
						public void onFacebookError(FacebookError error) {
							user.setAllowFB(false);
							((CheckBox) findViewById(R.id.fbAccessCheckBox))
									.setChecked(false);
							Toast.makeText(getBaseContext(),
									getString(R.string.fbError),
									Toast.LENGTH_LONG).show();
						}

						@Override
						public void onError(DialogError e) {
							user.setAllowFB(false);
							((CheckBox) findViewById(R.id.fbAccessCheckBox))
									.setChecked(false);
							Toast.makeText(getBaseContext(),
									getString(R.string.fbError),
									Toast.LENGTH_LONG).show();
						}

						@Override
						public void onCancel() {
							user.setAllowFB(false);
							((CheckBox) findViewById(R.id.fbAccessCheckBox))
									.setChecked(false);
						}
					});

		} else {

			String method = "DELETE";
			Bundle params = new Bundle();
			try {
				user.setAllowFB(false);
				((CheckBox) findViewById(R.id.fbAccessCheckBox))
						.setChecked(false);
				facebook.logout(getBaseContext());
				facebook.request("/me/permissions", params, method);
			} catch (Exception e) {
				// TODO: handle exception
			}
		}

	}

	/**
	 * An event handler for clicking on the check box for enabling and disabling
	 * the never show option.
	 * 
	 * @param view
	 */
	public void handleNeverCheckBox(View view) {
		user.setNeverShow(((CheckBox) findViewById(R.id.neverShowCheckBox))
				.isChecked());
	}

	/**
	 * Before the activity is destroyed the user's hate ingredients are updated
	 * with the current list
	 */
	@Override
	protected void onDestroy() {
		// TODO Auto-generated method stub
		user.setHateIngs(ingredientListAdapter.getList());
		super.onDestroy();
	}

	/**
	 * Class responsible for retrieving the user's basic information from
	 * Facebook
	 * 
	 * @author Rana
	 * 
	 */
	class FBUserInfo extends AsyncTask<Void, Void, String> {

		@Override
		protected void onPreExecute() {
			pdia = ProgressDialog.show(UserProfileActivity.this, "",
					getString(R.string.loadingUserdata), true);

		}

		@Override
		public void onCancelled() {
			pdia.dismiss();
		}

		@Override
		protected void onPostExecute(String result) {
			// After the result is returned, the user's name and facebook id are
			// set, then an attempt is made to retrieve the user's profile
			// picture. If an exception occurs the userName is set to "N\A"
			try {
				JSONObject obj = new JSONObject(result);
				user.setUserName(obj.optString("name"));
				user.setFacebookId(obj.optString("id"));
				setUserName(user.getUserName());
				new FBUserImage().execute();

			} catch (Exception e) {

				new FBUserImage().execute();
				user.setUserName("N\\A");
				((TextView) findViewById(R.id.userName)).setText("N\\A");
			}
		}

		@Override
		protected String doInBackground(Void... params) {
			try {
				String jsonUser = facebook.request("me");
				Log.e("In background", jsonUser);
				return jsonUser;
			} catch (Exception e) {
				Log.e("Exception", e.getMessage());
				return "N\\A";
			}
		}
	}

	/**
	 * This class is reponsible for retrieving the user's profile picture from
	 * facebook
	 * 
	 * @author Rana
	 * 
	 */
	class FBUserImage extends AsyncTask<Void, Void, String> {

		@Override
		protected void onPreExecute() {
			pdia.setMessage(getString(R.string.loadingUserImage));
		}

		@Override
		public void onCancelled() {
			pdia.dismiss();
		}

		@Override
		protected void onPostExecute(String result) {
			pdia.dismiss();
			try {
				setUserImage(user.getProfilePicture());

			} catch (Exception e) {
				setUserImage(null);
			}
		}

		@Override
		protected String doInBackground(Void... params) {
			try {
				getUserPic(user.getFacebookId());

			} catch (Exception e) {
				Log.e("Exception", e.getMessage());

			}
			return "";
		}
	}

	/**
	 * This class is responsible for getting the user's favorite dishes from the
	 * server
	 * 
	 * @author Rana
	 * 
	 */
	class getFavDishes extends AsyncTask<Void, Void, String> {

		private ProgressDialog pdiaUpdate;

		@Override
		protected void onPreExecute() {
			pdiaUpdate = ProgressDialog.show(UserProfileActivity.this, "",
					getString(R.string.loadingUserdata), true);
		}

		@Override
		public void onCancelled() {
			pdiaUpdate.dismiss();
		}

		@Override
		protected void onPostExecute(String result) {
			pdiaUpdate.dismiss();
			initGallery();
			Log.e("User Fav dishes status", result);
		}

		@Override
		protected String doInBackground(Void... params) {
			String input = "";
			try {
				Log.e("Request", "request made");
				HttpClient httpclient = new DefaultHttpClient();
				HttpPost httppost = new HttpPost("http://" + Main.SERVER
						+ "/user_info/get_fav_dishes");
				List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(
						1);
				nameValuePairs.add(new BasicNameValuePair("userId",
						Main.android_id));

				httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));

				// Execute HTTP Post Request
				HttpResponse response = httpclient.execute(httppost);

				/* Checking response */
				if (response != null) {
					HttpEntity entity = response.getEntity();
					input = ServerUtilities.getContentFromStream(entity
							.getContent());
					Log.e("The response is ", input);
					try {
						JSONArray jsonArr = new JSONArray(input);
						for (int i = 0; i < jsonArr.length(); i++) {
							Log.e("JsonArr", "Arr not zero");
							JSONObject current = jsonArr.getJSONObject(i);
							String name = current.getString("name");
							String key = current.getString("key");
							Bitmap bitmap = null;
							try {

								bitmap = BitmapFactory
										.decodeStream(new ByteArrayInputStream(
												Base64.decode(current
														.getString("img"),
														Base64.DEFAULT)));

							} catch (Exception e) {
								bitmap = null;
							}
							if (!user.likesDishByKey(key))
								user.addToFavorites(name, key, bitmap);

						}
					} catch (Exception e) {
						Log.e("AFter got input Excpetion", e.getMessage());
						Log.e("Response", input);
						runOnUiThread(new Runnable() {

							@Override
							public void run() {
								Toast.makeText(getBaseContext(),
										getString(R.string.getFavoritesError),
										Toast.LENGTH_LONG).show();

							}
						});
					}
				}
			} catch (Exception e) {
				Log.e("In response exception", e.getMessage());
				Log.e("Response", input);
				runOnUiThread(new Runnable() {

					@Override
					public void run() {
						Toast.makeText(getBaseContext(),
								getString(R.string.getFavoritesError),
								Toast.LENGTH_LONG).show();

					}
				});
			}
			return input;
		}
	}

	class IngredientsAsyncTask extends AsyncTask<Void, Void, String> {

		private ProgressDialog pd;

		@Override
		protected void onPreExecute() {
			super.onPreExecute();
			pd = ProgressDialog.show(UserProfileActivity.this, "",
					getString(R.string.getting_all_ingredients), true);
		}

		@Override
		protected String doInBackground(Void... params) {
			ingredients = Ingredients.getAll();
			return "";
		}

		@Override
		protected void onCancelled() {
			super.onCancelled();
			pd.dismiss();
		}

		@Override
		protected void onPostExecute(String result) {
			// super.onPostExecute(result);
			ingredients.removeAll(user.getHateIngs());
			initIngredientsField();
			pd.dismiss();
		}

	}

}
